import moment from "moment";
import { plantInfoList } from "./plant";
import { zombieList, createZombieTide } from "./zombie";
import { protectDurationCalc } from "./utils";
import { gameConfig } from "./config";
import { calc } from "a-calc";

const defaultBattle = {
    id: '',
    atk: { // 进攻方信息
        name: "zombie",
        zombieList: [],
        info: '',
        id: '',
        zombieInfoJson: '',
    },
    def: { // 防守方信息
        name: "user",
        plantList: [],
        info: '',
        id: '',
    },
    start: '',  // 开始时间
    // moment().format("YYYY-MM-DD HH:mm:ss")
    end: '',  // 结束时间
    timeLine: [],  // 时间轴 包含所有细节 包括僵尸的位置
    log: [],  // 日志 只记录基本信息
    lostPlant: [ // 阵亡植物
        // {
        //     name: '',
        //     num: 0,
        // }
    ], 
    lostSun: 0, // 损失阳光 僵尸抵达终点时扣除
    result: '', // 结果文本
    battleDuration: '', // 战斗时长
    isDay: true, // 是否为白天
}

const createBattle = (plantList, lv = 1, atkZombie = []) => {
    // atkZombie 不完整
    let info = defaultBattle
    let _info = JSON.parse(JSON.stringify(info))
    let atkZombieList
    if (atkZombie && atkZombie.length) { // 指定僵尸
        atkZombieList = atkZombie.map((i, index) => {
            const newZombieItem = zombieList.find(j => j.id == i.id)
            if (i.hp) newZombieItem.hp = i.hp // 指定的僵尸存在属性 继承
            newZombieItem.index = index
            newZombieItem.buff = []
            return newZombieItem
        })
    }
    else atkZombieList = createZombieTide(lv)
    let battleInfo = battleInfoFormat(plantList, atkZombieList)
    _info.atk.info = battleInfo.atkInfo
    _info.def.info = battleInfo.defInfo
    _info.atk.zombieInfoJson = battleInfo.zombieInfoJson
    _info.atk.zombieList = formatZombieList(atkZombieList)
    _info.def.plantList = formatPlantList(plantList)
    _info.isDay = moment().hour() < 18 && moment().hour() > 6 // 当前是否为白天
    _info.timeLine.push('一大波僵尸即将来临...\n' + moment().format("YYYY-MM-DD HH:mm:ss"))
    _info.log.push('一大波僵尸即将来临...\n' + moment().format("YYYY-MM-DD HH:mm:ss"))
    _info.def.plantList.forEach(plant => {
        if (_info.isDay && plant.sleep) { // 白天蘑菇睡眠
            _info.timeLine.push(`${plant.battleId} (${plant.x},${plant.y}) 睡着了`)
            _info.log.push(`${plant.battleId} (${plant.x}, ${plant.y}) 睡着了`)
        }
    })
    // let atkPlantLocation = info.def.plantList.map(i => `(${i.x},${i.y}) atkRange: ${i.atkRange} ${i.battleId}`)
    // let zombieLocation = info.atk.zombieList.map(i => `(${i.x},${i.y}) id: ${i.battleId}`)
    // console.log('atkPlantLocation', atkPlantLocation);
    // console.log('zombieLocation', zombieLocation);
    return _info
}

const formatZombieList = (list = []) => {
    let res = list.map((i) => {
        i.y = Math.floor(Math.random() * 5 + 1) // 初始y坐标范围 1 ~ 5
        i.x = 10 // 僵尸初始x坐标10; 1 ~ 9为可种植区域; 10为花园外 不在植物攻击范围
        i.atkInterval = 0 // 当前攻击间隔 为0后可攻击
        i.nextMoveTime = i.speed // 下次移动时间
        return i
    })
    res = JSON.parse(JSON.stringify(res))
    let result = []
    res.map(i => {
        let has = result.length != 0 && result.find(j => j.y == i.y && j.x == i.x) != null
        if (has) {
            let flag = 0
            while (has) {
                i.y = i.y + 1
                if (i.y > 5) {
                    i.y = 1
                    i.x = i.x + 1
                }
                flag += 1
                if (flag > 100) has = false // 防止无限循环
                else {
                    has = result.length != 0 && result.find(j => j.y == i.y && j.x == i.x) != null
                }
                if (result.length == 0 || !has) {
                    result.push(i)
                }
            }
        }
        else {
            result.push(i)
        }
    })
    result = result.map((i, index) => {
        i.battleId = i.name + '（#' + index + '）'
        return i
    })
    list = []
    return result
}

const formatPlantList = (plantList) => {
    let list = plantList.sort((a, b) => {
        return a.location - b.location
    })
    let x = 1 // 当前排列的植物的x坐标 同一x坐标最多存在5个植物
    let y = 1 // 当前y坐标
    let res = list.map((i, index) => {
        if (y == 6) {
            y = 1
            x = x + 1
        }
        i.x = x
        i.y = y
        y = y + 1
        i.atkInterval = 0 // 攻击间隔 攻击间隔为0时可进行攻击
        i.battleId = i.name + '（#' + index + '）'
        return i
    })
    return res
}

const createMidwayJoinZombie = (zombieId, x, y, index) => {
    // 创建中途加入的僵尸 并返回
    let info = zombieList.find(i => i.id == zombieId)
    let zombie = {
        ...info,
        x,
        y,
        battleId: info.name + '（#' + index + '）',
        atkInterval: 0,
        nextMoveTime: info.speed,
        buff: [],
    }
    return zombie
}

const battleTimerCallback = (info, targetTime) => {
    let { atk, def, timeLine = [], log = [], lostPlant = [], isDay = true, sunTotal = null } = info
    let { plantList } = def
    let midwayJoinZombie = [] // 中途加入的僵尸 插入后重置
    let zombieDamageList = [] // 僵尸受到伤害队列 再执行完僵尸逻辑后进行结算 用于处理僵尸逻辑中 僵尸受到的伤害结算
    let resultZombieList = atk.zombieList.map(i => {
        i.atkInterval -= 100
        if (i.skill) {
            if (!i.skill.isUsed) {
                if (i.skill.id == 'fn' && i.hp <= 130) { // 读报僵尸
                    i.skill.isUsed = true
                    i.speed = 2000 // 愤怒后移速翻倍
                    let timeLineItem = `${i.battleId} 被激怒了 移速大幅增加`
                    timeLine.push(timeLineItem)
                    log.push(timeLineItem)
                }
                else if (i.skill.id == 'zh' && i.hp <= 130) { // 舞王僵尸
                    i.skill.isUsed = true
                    if (i.x > 1) midwayJoinZombie.push(createMidwayJoinZombie(9, i.x - 1, i.y, atk.zombieList.length + midwayJoinZombie.length)) // 舞王僵尸不在最前一个
                    midwayJoinZombie.push(createMidwayJoinZombie(9, i.x + 1, i.y, atk.zombieList.length + midwayJoinZombie.length))
                    if (i.y > 1) midwayJoinZombie.push(createMidwayJoinZombie(9, i.x, i.y - 1, atk.zombieList.length + midwayJoinZombie.length)) // 舞王僵尸不在最上方
                    if (i.y < 5) midwayJoinZombie.push(createMidwayJoinZombie(9, i.x, i.y + 1, atk.zombieList.length + midwayJoinZombie.length)) // 舞王僵尸不在最下方
                    let timeLineItem = `${i.battleId} 在四周召唤出了多个伴舞僵尸`
                    timeLine.push(timeLineItem)
                    log.push(timeLineItem)
                }
                else if (i.skill.id == 'xg' && (i.hp <= 750 || i.x <= 5)) { // 巨人僵尸 自身生命值小于一半 或 自身x坐标小于5
                    i.skill.isUsed = true
                    midwayJoinZombie.push(createMidwayJoinZombie(11, i.x - 2, i.y, atk.zombieList.length + midwayJoinZombie.length))
                    let timeLineItem = `${i.battleId} 在(${i.x}, ${i.y})投掷了小鬼僵尸，小鬼僵尸在(${i.x - 2}, ${i.y})落地`
                    timeLine.push(timeLineItem)
                    log.push(timeLineItem)
                }
            }
        }
        if (i.buff && i.buff.length) { // 该僵尸的状态
            i.buff = i.buff.filter(buff => buff.duration > 0) // 清除已经结束的buff
            i.buff = i.buff.map(buff => {
                buff.duration = buff.duration - 100
                return buff
            })
        }
        let hasPlantIndex = plantList.findIndex(j => j.x == i.x && j.y == i.y && !j.isNotAttack)
        if (hasPlantIndex > -1) {
            // 攻击逻辑
            if (i.atkInterval <= 0) { // 可攻击
                if (i.buff && i.buff.length) { // 该僵尸的状态
                    let hasButter = i.buff.find(buff => buff.name == 'butter') // 被黄油固定了 无法攻击
                    if (hasButter) return i
                }
                if (i.skill) { // 该僵尸存在技能
                    if (!i.skill.isUsed) { // 未使用
                        if (i.skill.id == 'cg') { // 撑杆僵尸
                            i.skill.isUsed = true
                            i.speed = 4000 // 撑杆跳后移动速度变慢
                            i.x = i.x - 1
                            i.atkInterval = i.interval
                            let timeLineItem = `${i.battleId} 撑杆跳越过了 ${plantList[hasPlantIndex].battleId} 当前位置(${i.x},${i.y})`
                            timeLine.push(timeLineItem)
                            log.push(timeLineItem)
                            if (i.x <= 0) {
                                timeLineItem = `${i.battleId} 抵达了${def.name}的家...`
                                timeLine.push(timeLineItem)
                                log.push(timeLineItem)
                                i.isComing = true // 僵尸到达花园
                            }
                            return i
                        }
                    }
                }
                plantList[hasPlantIndex].hp = plantList[hasPlantIndex].hp - i.atk
                i.atkInterval = i.interval
                let timeLineItem = `${i.battleId} 在(${i.x}, ${i.y})啃食${plantList[hasPlantIndex].battleId} 剩余血量: ${plantList[hasPlantIndex].hp}`
                if (plantList[hasPlantIndex].skill) { // 僵尸攻击植物时 植物触发的技能
                    plantList[hasPlantIndex].skill.map(skill => {
                        if (skill.name == 'garlic') {
                            i.y = i.y == 1 ? 2 : i.y == 5 ? 4 : i.y + (Math.random() > 0.5 ? 1 : -1)
                            timeLineItem = `${i.battleId} 啃了一口${plantList[hasPlantIndex].battleId} 直接吐了，远离了大蒜 当前位置(${i.x}, ${i.y})`
                            let hasIceBuffIndex = i.buff.findIndex(j => j.name == 'ice') // 存在冰霜效果 则重置
                            if (hasIceBuffIndex > -1) i.buff.splice(hasIceBuffIndex, 1)
                            i.buff.push({
                                name: 'ice',
                                slowSpeed: 1.2, // 减速效果
                                duration: 2000, // 剩余持续时间
                            })
                        }
                    })
                }
                if (plantList[hasPlantIndex].hp <= 0) {
                    i.atkInterval = 0
                    i.nextMoveTime = targetTime + i.speed
                    timeLineItem = `${i.battleId} 吃掉了${plantList[hasPlantIndex].battleId}`
                    lostPlant.push({
                        name: plantList[hasPlantIndex].name,
                        id: plantList[hasPlantIndex].id
                    })
                    // 植物被吃掉时 植物触发的技能
                    if (plantList[hasPlantIndex].skill && plantList[hasPlantIndex].skill.length) {
                        plantList[hasPlantIndex].skill.map(skill => {
                            if (skill.name == 'boomNut') { // 爆炸坚果
                                let aoeRange = 1 // 杀伤范围
                                let boomZombieLocation = [i.x, i.y] // 爆炸中心位置
                                let aroundZombie = atk.zombieList.filter(j => { // 爆炸范围内的僵尸
                                    let xDistance = Math.abs(boomZombieLocation[0] - j.x)
                                    let yDistance = Math.abs(boomZombieLocation[1] - j.y)
                                    return xDistance <= aoeRange && yDistance <= aoeRange
                                })
                                aroundZombie.map(j => { // 加入僵尸伤害结算队列
                                    zombieDamageList.push({
                                        timeLineItem: `${j.battleId} 被爆炸坚果烧焦了 剩余血量 ${j.hp - skill.atk}`,
                                        damage: skill.atk,
                                        zombieId: j.battleId,
                                    })
                                })
                            }
                        })
                    }
                    plantList = plantList.filter(j => j.battleId != plantList[hasPlantIndex].battleId) // 移除被吃掉的植物
                }
                timeLine.push(timeLineItem)
                log.push(timeLineItem)
                // console.log(timeLineItem, targetTime)
            }
        }
        else {
            if (i.x <= 0) return i // 到达花园 直接返回
            // 行动逻辑
            if (i.nextMoveTime <= targetTime) {
                let moveLong = 1 // 僵尸移动距离
                let buffText = [] // 僵尸受到的buff 文字表述
                let zombieSpeed = i.speed // 僵尸移动需要花费的时间
                if (i.buff && i.buff.length) {
                    i.buff.map(buff => {
                        if (buff.name == 'ice') {
                            zombieSpeed = zombieSpeed * buff.slowSpeed
                            buffText.push(`被减速了`)
                        }
                        else if (buff.name == 'butter') {
                            moveLong = 0
                            buffText.push(`被黄油固定住了无法动弹`)
                        }
                    })
                }
                i.x = i.x - moveLong
                buffText = buffText.join('、')
                i.nextMoveTime = i.nextMoveTime + zombieSpeed
                let timeLineItem = `${i.battleId}${buffText} ${moveLong > 0 ? '缓缓向花园移动...' : ''} 当前位置(${i.x},${i.y}) ${targetTime}`
                if (i.x <= 0) {
                    timeLineItem = `${i.battleId} 抵达了${def.name}的家...`
                    timeLine.push(timeLineItem)
                    i.isComing = true // 僵尸到达花园
                    log.push(timeLineItem)
                }
                // console.log(timeLineItem, targetTime)
                
            }
        }
        return i
    })
    if (zombieDamageList.length) { // 执行僵尸伤害结算队列 用于处理遍历僵尸逻辑时 僵尸受到的伤害
        zombieDamageList.map(item => {
            let index = resultZombieList.findIndex(i => i.battleId == item.zombieId)
            if (index > -1) {
                resultZombieList[index].hp = resultZombieList[index].hp - item.damage
                timeLine.push(item.timeLineItem)
                log.push(item.timeLineItem)
            }
        })
        zombieDamageList = []
    }
    resultZombieList = resultZombieList.filter(i => { // 剔除 已抵达的 已阵亡的 僵尸
        if (i.isComing) {
            info.lostSun += i.loss // 计算损失阳光
            if (info.isComingZombie) info.isComingZombie.push(i)
            else info.isComingZombie = [i]
        }
        if (i.hp <= 0) {
            let timeLineItem = `${i.battleId} 倒下了...`
            timeLine.push(timeLineItem)
            log.push(timeLineItem)
        } 
        return !i.isComing && i.hp > 0
    })
    if (midwayJoinZombie.length) { // 存在中途加入的僵尸
        resultZombieList = resultZombieList.concat(midwayJoinZombie)
        midwayJoinZombie = []
    }
    // 攻击类型植物
    let resultPlantList = plantList.map(i => {
        if (i.buff && i.buff.length) { // 植物的buff
            i.buff = i.buff.filter(buff => buff.duration == '无限' || buff.duration > 0) // 清除已经结束的buff
            i.buff = i.buff.map(buff => {
                buff.duration = buff.duration == '无限' ?  '无限' : buff.duration - 100
                return buff
            })
        }
        if (i.type != 3) return i // 非攻击类型植物 直接返回
        if (isDay && i.sleep) return i // 白天睡觉的植物 直接返回
        i.atkInterval -= 100
        let hasCanAtk = i.atkInterval <= 0
        if (hasCanAtk) {
            let hasCanAtkZombieIndex = []
            let damage = i.atk // 本次攻击植物造成的伤害
            if (i.atkRange == 'line') { // 整行攻击中最前的敌人
                let firstIndex = resultZombieList.findIndex(j => j.y == i.y)
                if (firstIndex > -1) {
                    let resultIndex = resultZombieList.reduce((minIndex, item, index) => {
                        if (item.x < resultZombieList[minIndex].x && item.y == i.y) {
                            return index
                        }
                        else {
                            return minIndex
                        }
                    }, firstIndex)
                    hasCanAtkZombieIndex.push(resultIndex)
                }
            }
            else if (i.atkRange == 'front') { // 前方范围攻击 i.range为植物攻击距离
                let firstIndex = resultZombieList.findIndex(j => j.y == i.y && j.x <= (i.x + i.range))
                if (firstIndex > -1) {
                    let resultIndex = resultZombieList.reduce((minIndex, item, index) => {
                        if (item.y == i.y && item.x <= (i.x + i.range) && item.x < resultZombieList[minIndex].x) {
                            return index
                        }
                        else {
                            return minIndex
                        }
                    }, firstIndex)
                    hasCanAtkZombieIndex.push(resultIndex)
                }
            }
            if (i.skill && i.skill.length) { // 攻击前阶段 植物技能
                // 待开发
            }
            // 使用特殊技能代替普通攻击
            if (i.hasSkillAction) {
                if (i.hasSkillAction.name == 'star') {
                    let zombieNumber = resultZombieList.length
                    // 随机攻击5个僵尸
                    for (let index = 0; index < 5; index++) {
                        let zombieIndex = Math.floor(Math.random() * zombieNumber)
                        hasCanAtkZombieIndex.push(zombieIndex)
                    }
                    let timeLineItem = `${i.battleId} 同时向四周发射出多枚星星`
                    timeLine.push(timeLineItem)
                    log.push(timeLineItem)
                    hasCanAtkZombieIndex.map((atkIndex) => {
                        let starDamage = i.hasSkillAction.atk // 星星造成的伤害
                        // 僵尸存在尖刺buff 受到追加伤害
                        if (resultZombieList[atkIndex].buff && resultZombieList[atkIndex].buff.length && resultZombieList[atkIndex].buff.find(j => j.name == 'cactus')) {
                            starDamage = starDamage + resultZombieList[atkIndex].buff.find(j => j.name == 'cactus').damage
                        }
                        resultZombieList[atkIndex].hp = resultZombieList[atkIndex].hp - starDamage
                        let timeLineItem = `流星击中了${resultZombieList[atkIndex].battleId} 剩余血量 ${resultZombieList[atkIndex].hp}`
                        timeLine.push(timeLineItem)
                    })
                    delete i.hasSkillAction
                }
                i.atkInterval = i.interval // 进入攻击间隔
            }
            // 攻击范围内存在僵尸 普通攻击
            else if (hasCanAtkZombieIndex.length) {
                hasCanAtkZombieIndex.map(hasAtkZombieIndex => {
                    let timeLineItem = ''
                    let buffText = '' // 植物buff效果 文字表述
                    let miss = false // 攻击未命中
                    if(i.hitRate) { // 植物存在命中率 0.5即有50%概率命中
                        let random = Math.random()
                        miss = random > i.hitRate
                    }
                    if (i.isAoeDamage) { // 对僵尸造成范围伤害
                        let aoeRange = i.isAoeDamageRange || 1 // 杀伤范围
                        let atkZombieLocation = [resultZombieList[hasAtkZombieIndex].x, resultZombieList[hasAtkZombieIndex].y] // 被攻击的僵尸的位置
                        let aroundZombie = resultZombieList.filter(j => { // 与被攻击的僵尸的x,y距离相差aoeRange的僵尸
                            let xDistance = Math.abs(atkZombieLocation[0] - j.x)
                            let yDistance = Math.abs(atkZombieLocation[1] - j.y)
                            return !(j.x == atkZombieLocation[0] && j.y == atkZombieLocation[1]) && xDistance <= aoeRange && yDistance <= aoeRange
                        })
                        if (aroundZombie && aroundZombie.length) {
                            resultZombieList = resultZombieList.map(_zombie => {
                                if (aroundZombie.findIndex(j => j.battleId == _zombie.battleId) > -1) {
                                    _zombie.hp -= miss ? 0 : damage
                                    _zombie.isHandleAoe = true // 标记该僵尸受到了aoe伤害
                                }
                                return _zombie
                            })
                        }
                    }
                    if (i.skill && i.skill.length) { // 攻击后结算阶段 植物技能
                        i.skill.map(skill => {
                            if (skill.name == 'ice') { // 冰霜
                                let hasIceBuffIndex = resultZombieList[hasAtkZombieIndex].buff.findIndex(j => j.name == 'ice')
                                // 存在冰霜效果 则重置
                                if (hasIceBuffIndex > -1) resultZombieList[hasAtkZombieIndex].buff.splice(hasIceBuffIndex, 1)
                                resultZombieList[hasAtkZombieIndex].buff.push({
                                    name: 'ice',
                                    slowSpeed: skill.slowSpeed, // 减速效果
                                    duration: skill.slowTime, // 剩余持续时间
                                })
                                buffText = '被减速了'
                            }
                            else if (skill.name == 'cactus') { // 仙人掌 尖刺效果
                                let hasCactusBuffIndex = resultZombieList[hasAtkZombieIndex].buff.findIndex(j => j.name == 'cactus')
                                // 存在尖刺效果 则重置
                                if (hasCactusBuffIndex > -1) resultZombieList[hasAtkZombieIndex].buff.splice(hasCactusBuffIndex, 1)
                                resultZombieList[hasAtkZombieIndex].buff.push({
                                    name: 'cactus',
                                    duration: skill.duration, // 剩余持续时间
                                    damage: skill.atk, // 尖刺追加伤害
                                })
                                buffText = '被尖刺穿透了'
                            }
                            else if (skill.name == 'butter') { // 玉米投手 投掷黄油
                                let hasAction = false
                                i.buff = i.buff && i.buff.length ? i.buff : [] // 防止buff为不存在
                                let hasButterBuffIndex = i.buff.findIndex(j => j.name == 'butter')
                                if (hasButterBuffIndex > -1) { // 玉米投手现在存在黄油buff
                                    i.buff[hasButterBuffIndex].value++
                                    if (i.buff[hasButterBuffIndex].value == skill.needTimes) { // 叠满5层黄油buff 该次攻击投掷出黄油
                                        hasAction = true
                                        i.buff[hasButterBuffIndex].value = 0 // 重置层数
                                    }
                                }
                                else { // 不存在黄油buff 添加
                                    i.buff.push({
                                        name: 'butter',
                                        value: 1, // 黄油标记数
                                        duration: '无限', // 持续时间 无限
                                    })
                                }
                                if (hasAction) { // 触发了效果 投掷出黄油
                                    let hasButterBuffIndex = resultZombieList[hasAtkZombieIndex].buff.findIndex(j => j.name == 'butter')
                                    // 存在黄油效果 则重置
                                    if (hasButterBuffIndex > -1) resultZombieList[hasAtkZombieIndex].buff.splice(hasButterBuffIndex, 1)
                                    resultZombieList[hasAtkZombieIndex].buff.push({
                                        name: 'butter',
                                        duration: skill.duration, // 剩余持续时间
                                    })
                                    buffText = '被黄油固定住了'
                                    damage = skill.atk // 黄油伤害
                                }
                            }
                            else if (skill.name == 'star') { // 杨桃技能
                                i.buff = i.buff && i.buff.length ? i.buff : [] // 防止buff为不存在
                                let hasStarBuffIndex = i.buff.findIndex(j => j.name == 'star')
                                if (hasStarBuffIndex > -1) { // 杨桃累计攻击次数buff
                                    i.buff[hasStarBuffIndex].value = i.buff[hasStarBuffIndex].value + 1
                                    if (i.buff[hasStarBuffIndex].value >= skill.needTimes) { // 累计攻击次数 达到5次
                                        i.hasSkillAction = { name: 'star', atk: skill.atk } // 下次普通使用特殊技能代替普通攻击
                                        i.buff.splice(hasStarBuffIndex, 1) // 移除累计攻击次数buff
                                    }
                                }
                                else { // 不存在攻击层数 添加
                                    i.buff.push({
                                        name: 'star',
                                        value: 1, // 攻击层数
                                        duration: '无限', // 持续时间 无限
                                    })
                                }
                            }
                            else if (skill.name == 'squash' || skill.name == 'cherryBomb') { // 灰烬相关技能 在攻击后立即阵亡 窝瓜、樱桃炸弹
                                i.hp = 0
                                lostPlant.push({
                                    name: i.name,
                                    id: i.id
                                })
                            }
                        })
                    }
                    // 僵尸存在尖刺buff 受到追加伤害
                    if (resultZombieList[hasAtkZombieIndex].buff && resultZombieList[hasAtkZombieIndex].buff.length && resultZombieList[hasAtkZombieIndex].buff.find(j => j.name == 'cactus')) {
                        damage = damage + resultZombieList[hasAtkZombieIndex].buff.find(j => j.name == 'cactus').damage
                    }
                    resultZombieList[hasAtkZombieIndex].hp -= miss ? 0 : damage // 计算造成伤害
                    timeLineItem = `${i.battleId}${i.atkActionText}${resultZombieList[hasAtkZombieIndex].battleId}(${resultZombieList[hasAtkZombieIndex].x}, ${resultZombieList[hasAtkZombieIndex].y}) ${miss ? '但是没有造成伤害' : ''}${buffText ? buffText : ''} 剩余血量: ${resultZombieList[hasAtkZombieIndex].hp}`
                    timeLine.push(timeLineItem)
                    resultZombieList = resultZombieList.map(j => { // 去除之前附加的aoe标记 保存相关日志
                        if (j.isHandleAoe) {
                            delete j.isHandleAoe
                            let timeLineItemAoeAtk = `${j.battleId} 受到 ${i.battleId} 的 溅射伤害 剩余血量 ${j.hp}`
                            timeLine.push(timeLineItemAoeAtk)
                            log.push(timeLineItemAoeAtk)
                        }
                        return j
                    })
                })
                i.atkInterval = i.interval // 进入攻击间隔
            }
            // 攻击范围内不存在僵尸 继续等待
            else {
                i.atkInterval = 0
            }
        }
        return i
    })
    resultZombieList = resultZombieList.filter(i => { // 剔除 已抵达的 已阵亡的 僵尸
        if (i.isComing) {
            info.lostSun += i.loss // 计算损失阳光
            if (info.isComingZombie) info.isComingZombie.push(i)
            else info.isComingZombie = [i]
        }
        if (i.hp <= 0) {
            let timeLineItem = `${i.battleId} 倒下了...`
            timeLine.push(timeLineItem)
            log.push(timeLineItem)
        } 
        return !i.isComing && i.hp > 0
    })
    resultPlantList = resultPlantList.filter(i => { // 剔除已阵亡的植物
        return i.hp > 0
    })
    let isEnd = resultZombieList.length == 0 || (resultPlantList.length == 0 && plantList.length)
    if (isEnd) {
        // 结束时间
        info.end = moment().format("YYYY-MM-DD HH:mm:ss")
        // 损失的阳光 限制
        if (sunTotal != null) {
            let maxLossSun = Math.floor(sunTotal * gameConfig.maxLostSun)
            info.lostSun = info.lostSun > maxLossSun ? maxLossSun : info.lostSun // 限制损失阳光
        }
        let resultLostPlant = []
        lostPlant.map(i => {
            let hasIndex = resultLostPlant.findIndex(j => j.id == i.id)
            if (hasIndex == -1) {
                i.num = 1
                resultLostPlant.push(i)
            }
            else resultLostPlant[hasIndex].num++
        })
        // 损失的植物
        info.lostPlant = resultLostPlant
        let lostPlantText = info.lostPlant.length ? info.lostPlant.map(i => `${i.name} x ${i.num}`).join('、') : '无'
        // 存活的僵尸
        info.isComingZombie = info.isComingZombie ? info.isComingZombie.map(item => { // 将存活僵尸的对战属性移除
            delete item.index
            delete item.buff
            delete item.x
            delete item.y
            delete item.atkInterval
            delete item.nextMoveTime
            delete item.battleId
            delete item.isComing
            return item
        }) : []
        // 战斗时长计算
        let randomTime = Math.random() * 2 + 8
        randomTime = randomTime.toFixed(2)
        let battleDuration = calc(`${moment(info.end, 'YYYY-MM-DD HH:mm:ss').diff(moment(info.start, 'YYYY-MM-DD HH:mm:ss'), 'seconds')} * ${randomTime} | <=2`)
        let result = [
            info.start,
            atk.name + '袭击了' + def.name + '的花园',
            '战斗时长：' + battleDuration + '秒',
            '敌方信息：' + atk.info,
            '我方信息：' + def.info,
            '损失植物：' + lostPlantText,
            '阳光池受损：' + info.lostSun,
        ]
        // 保护时间计算
        const { protectEndTime, protectDuration } = protectDurationCalc(info.lostSun, info.lostPlant, atk.name != '游荡的僵尸')
        if (protectDuration) {
            result.push('保护时间：' + protectDuration + '分钟')
            result.push('保护结束时间：' + protectEndTime)
        }
        // 奖励结算
        info = battleRewardCalc(info) 
        result.push(info.reward.def.str) // 防守方奖励信息
        result = result.join('\n')
        info.result = result
        info.battleDuration = battleDuration
        info.protectEndTime = protectEndTime || ''
    }
    info.atk.zombieList = resultZombieList
    info.def.plantList = resultPlantList
    return {
        info,
        isEnd
    }
}

// 战斗结束 奖励结算
const battleRewardCalc = (info) => {
    let { atk, lostPlant = [], lostSun = 0 } = info
    const reward = {
        atk: {
            items: [],
            undeadEssence: 0, // 不死精元数量
            str: '',
        },
        def: {
            items: [],
            str: '',
        }
    }
    // 防守方奖励计算
    let zombieInfoJson = JSON.parse(atk.zombieInfoJson)
    const isComingZombie = info.isComingZombie
    isComingZombie.map(i => {
        let hasIndex = zombieInfoJson.findIndex(j => j.id == i.id)
        if (hasIndex > -1) zombieInfoJson.splice(hasIndex, 1) // 剔除存活僵尸
    })
    let deadZombieStrength = zombieInfoJson.reduce((pre, item) => { // 阵亡僵尸整体强度估算
        return pre += item.loss
    }, 0)
    let defLotteryCount = Math.floor(deadZombieStrength / 100) // 抽奖次数 阵亡僵尸强度每100点进行一次抽奖
    if (defLotteryCount > 0) {
        let defLottery = handleDefLottery(defLotteryCount) // 防守奖励
        reward.def.items = reward.def.items.concat(defLottery)
        reward.def.str = '僵尸掉落物品：' + defLottery.map(item => `${item.name} x ${item.num}`).join('、')
    }
    else reward.def.str = '僵尸掉落物品：无'
    // 进攻方奖励计算
    let atkLotteryCount = 0
    if (lostPlant.length) atkLotteryCount += lostPlant.reduce((pre, item) => { // 每有一棵阵亡植物 进行一次抽奖
        return pre += item.num
    }, 0)
    if (lostSun > 0) atkLotteryCount += Math.floor(lostSun / 100) // 每损失100阳光 进行一次抽奖
    if (atkLotteryCount > 0) {
        let atkLottery = handleAtkLottery(atkLotteryCount)
        atkLottery.map(item => {
            if (item.type == 'undeadEssence') reward.atk.undeadEssence += item.value
            else if (item.type == 'item') reward.atk.items.push(item)
        })
        reward.atk.str = '袭击花园奖励结算：'
        if (reward.atk.items.length) reward.atk.str += reward.atk.items.map(item => `${item.name} x ${item.num}`).join('、')
        if (reward.atk.items.length && reward.atk.undeadEssence) reward.atk.str += '、'
        if (reward.atk.undeadEssence) reward.atk.str += `不死精元: ${reward.atk.undeadEssence}`
    }
    else reward.atk.str = '袭击花园奖励结算：无'
    info.reward = reward
    // console.log('reward', reward)
    return info
}

// 防守方奖池(原料)
const plantLotteryPool = [
    {
        itemId: 2,
        name: '银币',
        num: 5,
    },
    {
        itemId: 3,
        name: '金币',
        num: 1,
    },
    {
        itemId: 13,
        name: '磨损的银币',
        num: 10,
    },
    {
        itemId: 16,
        name: '报纸',
        num: 10,
    },
    {
        itemId: 17,
        name: '铁桶',
        num: 10,
    },
    {
        itemId: 27,
        name: '僵尸头骨',
        num: 10,
    },
    {
        itemId: 19,
        name: '阳光满屋图腾',
        num: 1,
    },
    {
        itemId: 11,
        name: '戴夫的礼物',
        num: 5,
    }
]

// 进攻方奖池(原料)
const zombieLotteryPool = [
    {
        type: 'undeadEssence',
        name: '不死精元',
        value: 1,
        num: 70,
    },
    {
        type: 'undeadEssence',
        name: '不死精元',
        value: 5,
        num: 10,
    },
    {
        type: 'undeadEssence',
        name: '不死精元',
        value: 10,
        num: 5,
    },
    {
        type: 'item',
        itemId: 18,
        name: '阳光瓶',
        num: 10,
    },
    {
        type: 'item',
        itemId: 32,
        name: '金盏花的礼盒',
        num: 5,
    }
]

// 防守方奖励
const handleDefLottery = (defLotteryCount) => {
    let resultLotteryPool = [] // 奖池
    let _ = JSON.parse(JSON.stringify(plantLotteryPool)) // 防止污染原数组
    _.map(i => {
        for (let index = 0; index < i.num; index++) {
            resultLotteryPool.push(i)
        }
    })
    const lottery = [] // 抽奖结果
    for (let i = 0; i < defLotteryCount; i++) {
        let index = Math.floor(Math.random() * resultLotteryPool.length)
        let item = resultLotteryPool[index]
        delete item.num
        lottery.push(item)
        resultLotteryPool.splice(index, 1)
    }
    const res = []
    lottery.map(i => {
        const hasIndex = res.findIndex(j => j.itemId == i.itemId)
        if (hasIndex > -1) res[hasIndex].num++
        else {
            i.num = 1
            res.push(i)
        }
    })
    return res
}

// 进攻方奖励
const handleAtkLottery = (atkLotteryCount) => {
    let resultLotteryPool = [] // 奖池
    let _ = JSON.parse(JSON.stringify(zombieLotteryPool)) // 防止污染原数组
    _.map(i => {
        for (let index = 0; index < i.num; index++) {
            resultLotteryPool.push(i)
        }
    })
    const lottery = [] // 抽奖结果
    for (let i = 0; i < atkLotteryCount; i++) {
        let index = Math.floor(Math.random() * resultLotteryPool.length)
        let item = resultLotteryPool[index]
        delete item.num
        lottery.push(item)
        resultLotteryPool.splice(index, 1)
    }
    const res = []
    lottery.map(i => {
        if (i.type == 'undeadEssence') res.push(i)
        else if (i.type == 'item') {
            const hasIndex = res.findIndex(j => j.type == 'item' && j.itemId == i.itemId)
            if (hasIndex > -1) res[hasIndex].num++
            else {
                i.num = 1
                res.push(i)
            }
        }
    })
    return lottery
}

const battleInfoFormat = (plantList, atkZombieList) => {
    plantList = JSON.parse(JSON.stringify(plantList))
    atkZombieList = JSON.parse(JSON.stringify(atkZombieList))
    let zombieInfoJson = JSON.stringify(atkZombieList)
    let atkInfo = []
    let defInfo = []
    let zombie = []
    let plant = []
    atkZombieList.map(i => {
        let hasIndex = zombie.findIndex(j => j.id == i.id)
        if (hasIndex == -1) {
            i.num = 1
            zombie.push(i)
        } 
        else zombie[hasIndex].num++
    })
    zombie.map(i => {
        atkInfo.push(`${i.name} x ${i.num}`)
    })
    plantList.map(i => {
        let hasIndex = plant.findIndex(j => j.id == i.id)
        if (hasIndex == -1) {
            i.num = 1
            plant.push(i)
        } 
        else plant[hasIndex].num++
    })
    plant.map(i => {
        defInfo.push(`${i.name} x ${i.num}`)
    })
    atkInfo = atkInfo.join('、')
    defInfo = defInfo.length ? defInfo.join('、') : '花园空空的'
    // 释放内存
    zombie = []
    plant = []
    
    return {
        atkInfo,
        defInfo,
        zombieInfoJson,
    }
}


export { 
    createBattle,
    battleTimerCallback,
    formatPlantList,
}